/**
 ** file - File operations
 *
 * The file module provides a File object for reading and writing to a file. A File
 * instance can be created directly or through use of the open function. 
 *
 */

define('oj/file', ['oj/os'], function (os) {
    
    var file = {
        
        /* Functions */
        open: function (path) {},
        
        /* Objects */
        File: File
        
    };
    
    
    var CONCAT_LIMIT = 90,
        URI_LIMIT    = 1801;
    
    
    file.open = function (path) {
        /**
         *> file.open('path')
         *
         * Returns a File instance.
         *
         *. var f = file.open('file.js');
         *
         */
        
        return (new File(path));
    }
    
    
    function File(path) {
        /**
         ** File - File object
         *
         */
        
        var self = this,
            closed = false;
        
        self.path = os.path.abspath(path);
        self.content = '';


        self.read = function (callback) {
            /**
             *> read(callback)
             *
             * Reads the file. In addition to the standard response properties, this
             * instance of File is returned as 'file'.
             *
             *. f.read(function (msg) {
             *.     if (!oj.isError(msg)) {
             *.         var read = msg.file.content;
             *.     }
             *. }
             * 
             */

            os.read(self.path, function (response) {
                if (response.hasOwnProperty('error')) {
                    if (typeof callback === 'function') {
                        response.file = self;
                        callback(response);
                    }
                    return;
                }
                self.content = response;
                closed = false;
                if (typeof callback === 'function') {
                    response = {
                        success: 'successfully read',
                        file: self
                    };
                    callback(response);
                }
            });

            return self;
        };


        self.write = function (string) {
            /**
             *> write('string')
             *
             * Write a string to the file. Due to buffering the string will not appear
             * in the server version of the file until flush() or close() are called.
             *
             *. f.write('append this to the files content');
             * 
             */

            self.content += string;
        };


        self.flush = function (callback) {
            /**
             *> flush(callback)
             *
             * Flush the internal buffer.
             
             */

            var MAX_LEN = URI_LIMIT - self.path.length,
                data = encodeURIComponent(self.content),
                len, parts = [],
                resps = 0,
                i;

            // chop data up into parts for sending 
            while (data.length > MAX_LEN) {
                len = MAX_LEN;

                // care we don't chop escapes (~X) or encodings (%40, %C3)
                while (data.charAt(len - 1) === '~') {
                    len--;
                }
                while (data.lastIndexOf('%', len) > len - 3) {
                    len = data.lastIndexOf('%', len) - 1;
                }
                parts.push(data.slice(0, len));
                data = data.slice(len);
            }

            parts.push(data);

            var concatParts = function (concat) {
                if (typeof concat === 'undefined' || concat.hasOwnProperty('success')) {
                    if (parts.length > 0) {
                        i = parts.slice(0, CONCAT_LIMIT);
                        parts = parts.slice(CONCAT_LIMIT);
                        if (parts.length > 0) {
                            resps = i[i.length - 1] + '.0';
                            i.push(resps);
                            parts.unshift(resps);
                        }
                        os.concat(i, true, concatParts);
                        return;
                    }
                    if (typeof callback === 'function') callback({
                        success: true,
                        file: self
                    });
                    return;
                }
                if (typeof callback === 'function') {
                    concat.file = self;
                    callback(concat);
                }
            };
            
            var multiWrite = function (write) {
                if (write.hasOwnProperty('error')) {
                    resps += parts.length + 1;
                    if (typeof callback === 'function') {
                        write.file = self;
                        callback(write);
                    }
                }
                if (typeof callback === 'function') callback({
                    multipart: resps,
                    file: self
                });
                
                if (++resps !== parts.length) return;
                for (i = 0; i < parts.length; i++) {
                    parts[i] = '/tmp' + self.path + '.' + i;
                }
                parts.push(self.path);
                concatParts();
            };
            
            if (parts.length > 1) {
                if (typeof callback === 'function') callback({
                    'multiparts': parts.length
                });
                // multipart write
                for (i = 0; i < parts.length; i++) {
                    os.write('/tmp' + self.path + '.' + i, parts[i], multiWrite);
                }
                return;
            }

            // single part write
            os.write(self.path, parts[0], function (write) {
                if (typeof callback === 'function') {
                    if (write.hasOwnProperty('error')) {
                        write.file = self;
                        return callback(write);
                    }
                    callback({
                        success: true,
                        file: self
                    });
                }
            });
        };


        self.close = function (callback) {
            /**
             *> close(callback)
             *
             * Close the file. A closed file cannot be read or written to any more.
             */

            self.flush(function (m) {
                if (m.hasOwnProperty('success')) closed = true;
                if (typeof callback === 'function') callback(m);
            });
        };


        self.closed = function () {
            /**
             *> closed()
             *
             * Returns true if file is closed; otherwise false.
             
             */

            return closed;
        };


        self.truncate = function () {
            /**
             *> truncate()
             *
             * Truncates the files contents to an empty string.
             *
             */

            self.content = '';
        };


        function uriEncode(string) {
            /* Encodes a string to the URI allowable set.
             * 
             * A dictionary of common Javascript character sequences is applied
             * to achieve substituation compression. The reverse dictionary
             * substitutes the result back to the original string.
             */

            var subs = [
                [/~/gi, '~~'],
                [/_/gi, '~_'],
                [/!/gi, '~!'],
                [/\*/gi, '~*'],
                [/\n/gi, '!'],
                [/\+/gi, '*'],
                [/ /gi, '_'],
                [/([A-Z])/g, '~$1'],
                [/;/gi, 'A'],
                [/=/gi, 'B'],
                [/===/gi, 'C'],
                [/~!==/gi, 'D'],
                [/:/gi, 'E'],
                [/,/gi, 'F'],
                [/\[/gi, 'G'],
                [/\]/gi, 'H'],
                [/"/gi, 'I'],
                [/</gi, 'J'],
                [/>/gi, 'K'],
                [/\//gi, 'L'],
                [/{/gi, 'M'],
                [/}/gi, 'N'],
                [/\$/gi, 'O'],
                [/&&/gi, 'P'],
                [/\(/gi, 'Q'],
                [/#/gi, 'R'],
                [/\^/g, 'S'],
                [/\|\|/g, 'T'],
                [/\?/g, 'U'],
                [/\\/g, 'V']
            ],
                i;

            for (i = 0; i < subs.length; i++) {
                string = string.replace(subs[i][0], subs[i][1]);
            }

            return encodeURIComponent(string);
        };
    };
    
    
    return file;

});
